﻿////////////////////////////////////////////////////////////////////////////
//
//  Immense Engine Source File.
//  Copyright (C), Indloon 2012
// -------------------------------------------------------------------------
//  File name:   IeMath.h
//  Description: ieMath class and definations
//  Created:     8/12/2012 Genert Org
//  Compilers:   Visual C++ 2010 Express
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef __IMMENSEENGINE_MATH_H
#define __IMMENSEENGINE_MATH_H
#pragma once

class ieMath;
class ieRandom;
class ieVec2;
class ieVec3;
class ieVec4;
class ieColor;
class ieRect;


/*  1. D E F I N E S
	=========================================================================== */
	
#define M_E							2.71828182845904523536f
#define M_PI						3.14159265358979323846f
#define M_PI_TWO					6.28318530717958647692f
#define M_PI_HALF					1.57079632679489661923f
#define M_SQRT_TWO					1.41421356237309504880f
#define M_SQRT_THREE				1.73205080756887729352f
#define M_SQRT_1OVER2				0.70710678118654752440f
#define M_SQRT_1OVER3				0.57735026918962576450f
#define M_EPSILON					1.192092896e-07f
#define M_INFINITY					1e30f

#define CHARSIGNBITSET(c)			(((const uint)(c)) >> 7)
#define CHARSIGNBITNOTSET(c)		((~((const uint)(c))) >> 7)

#define SHORTSIGNBITSET(s)			(((const uint)(s)) >> 15)
#define SHORTSIGNBITNOTSET(s)		((~((const uint)(s))) >> 15)

#define INTSIGNBITSET(i)			(((const uint)(i)) >> 31)
#define INTSIGNBITNOTSET(i)			((~((const uint)(i))) >> 31)

#define FLOATSIGNBITSET(f)			((*(const uint *)&(f)) >> 31)
#define FLOATSIGNBITNOTSET(f)		((~(*(const uint *)&(f))) >> 31)

#define DEG2RAD(a)					((a) * M_PI / 180.0f)
#define RAD2DEG(a)					((a) * 180.0f / M_PI)

#define UNITS2METERS(x)				((x) * 0.0254f)
#define METERS2UNITS(x)				((x) * (1.0f / 0.0254f))

#define	FloatIsNan(x)			    (((*(const unsigned long *)&x) & 0x7f800000) == 0x7f800000)
#define FloatIsInf(x)			    (((*(const unsigned long *)&x) & 0x7fffffff) == 0x7f800000)
#define FloatIsInd(x)			    ((*(const unsigned long *)&x) == 0xffc00000)
#define	FloatIsDenormal(x)	        (((*(const unsigned long *)&x) & 0x7f800000) == 0x00000000 && \
								    ((*(const unsigned long *)&x) & 0x007fffff) != 0x00000000 ) 
#define FloatU32ExpMask				( 0xFF << 23 )
#define FloatU32FracMask			( ( 1 << 23 ) - 1 )
#define FloatU32SignMask			( 1 << 31 )

#define HasZero(v)                  (((v) - 0x01010101UL) & ~(v) & 0x80808080UL)
#define HasLess(x,n)                (((x)-~0UL/255*(n))&~(x)&~0UL/255*128)
#define HasMore(x,n)                (((x)+~0UL/255*(127-(n))|(x))&~0UL/255*128)
#define CountLess(x,n)              (((~0UL/255*(127+(n))-((x)&~0UL/255*127))&~(x)&~0UL/255*128)/128%255)
#define CountMore(x,n)              (((((x)&~0UL/255*127)+~0UL/255*(127-(n))|(x))&~0UL/255*128)/128%255)

template <class T> ILINE T			Min ( T x, T y) { return ( x < y ) ? x : y; }
template <class T> ILINE T			Max ( T x, T y) { return ( x > y ) ? x : y; }

template <class T> ILINE T			Min3 ( T x, T y, T z) { return ( x < y ) ? (( x < z ) ? x : z) : (( y < z ) ? y : z); }
template <class T> ILINE T			Max3 ( T x, T y, T z) { return ( x > y ) ? (( x > z ) ? x : z) : (( y > z ) ? y : z); }

template <class T> ILINE T			Square ( T x ) { return x * x; }
template <class T> ILINE T			Cube ( T x ) { return x * x * x; }

/*  2. C L A S S
	=========================================================================== */
	
class ieMath { // internal linkage
public:
    static uint16       swapU16( uint16 value );
	static uint32       swapU32( uint32 value );
	
    static float        SqrtFast( float x );                    // square root
    static float        Sqrt( float x );                        // square root
	static float        RSqrt( float x );                       // reciprocal square root, returns huge number when x == 0.0
    static float        Floor( float x );                       // returns the largest integer that is less than or equal to the given value
	static float        Round( float x );
	static float		Sin( float x );                         
	static float		Cos( float x );
	static void			SinCos( float x, float &s, float &c );
	static float		Tan( float x );
	static float		ASin( float x );
	static float		ACos( float x );
	static float		ATan( float x );
	static float		ATan( float y, float x );
	static float		Log( float x );
	static float		Exp( float x );
	static float		Pow( float x, float y );
	static float        Fabs( float x );
	static float        Frac( float x );
	
	static bool         IsPowerOfTwo( int i );
	
	static float        Impulse( float k, float x );
	static float        AlmostIdentity( float x, float m, float n );
	static float        CubicPulse( float c, float w, float x );
	
	static float		AngleNormalize360( float angle );
	static float		AngleNormalize180( float angle );
	static float		AngleDelta( float angle1, float angle2 );
	
	static char	        ClampChar( int value );
	static byte	        ClampByte( int value );
	static short        ClampShort( int value );
	static word	        ClampWord( int value );
	static int          ClampInt( int value, int min, int max );
	static float        ClampFloat( float value, float min, float max );
	
	static char			FloatToChar( float f );
	static byte			FloatToByte( float f );
	static short		FloatToShort( float f );
	static word			FloatToWord( float f );
	static int			FloatToInt( float f );
};


/*  2. F U N C T I O N S
	=========================================================================== */
	
// -------------------------------------------------------------------------------------------------------------
ILINE uint16 ieMath::swapU16( uint16 value )
{
    return ( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 );
}
// -------------------------------------------------------------------------------------------------------------
ILINE uint32 ieMath::swapU32( uint32 value )
{
    return ( ( value & 0x000000FF ) << 24) | ( ( value & 0x0000FF00 ) << 8 ) | ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::SqrtFast( float x )
{
#if defined SIMD_X86
	__m128 xmm;
	float  r;

	xmm = _mm_load_ss( &x );
	xmm = _mm_mul_ss( xmm, _mm_rsqrt_ss( xmm ) );
	_mm_store_ss( &r, xmm );

	return r;
#else

	float y;
	float r;
	int	  i;

	y = x * 0.5f;
	i = *reinterpret_cast<int*>( &x );
	i = 0x5F3759DF - ( i >> 1 );
	r = *reinterpret_cast<float*>( &i );
	r = r * ( 1.5f - y * r * r );
	r = r * ( 1.5f - y * r * r );
	r = r * x;

	return r;
	
#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Sqrt( float x )
{
    float halfx = 0.5f * x;
	long i = *reinterpret_cast<long*>( &x );
	
	i = 0x5f375a86- ( i>>1 ); 
	x = *reinterpret_cast<float*>( &i );
	
	x = x *( 1.5f - halfx * x * x );
	return x;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::RSqrt( float x )
{
#if defined SIMD_X86
	__m128 xmm;
	float   r;

	xmm = _mm_load_ss( &x );
	xmm = _mm_rsqrt_ss( xmm );
	_mm_store_ss( &r, xmm );

	return r;
#else

	float  y;
	float  r;
	int	   i;

	y = x * 0.5f;
	i = *reinterpret_cast<int *>( &x );
	i = 0x5F3759DF - ( i >> 1 );
	r = *reinterpret_cast<float*>( &i );
	r = r * ( 1.5f - y * r * r );
	r = r * ( 1.5f - y * r * r );

	return r;

#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Floor( float x )
{
    return ( floorf( x ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Round( float x )
{
    return ( floorf( x * 0.5f ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Sin( float x )
{
	return sinf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Cos( float x )
{
	return cosf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieMath::SinCos( float x, float &s, float &c ) 
{
#ifdef _MSC_VER
	__asm {
		fld			x
		mov			ecx, s
		mov			edx, c
		fsincos
		fstp		DWORD PTR [edx]
		fstp		DWORD PTR [ecx]
	}
#else

	s = sinf( x );
	c = cosf( x );

#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Tan( float x )
{
	return tanf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::ASin( float x )
{
	if( x <= -1.0f )
	{
		return -M_PI_HALF;
    }
	
	if( x >= 1.0f )
	{
		return M_PI_HALF;
	}

	return asinf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::ACos( float x )
{
	if ( x <= -1.0f )
	{
		return M_PI;
    }
	
	if ( x >= 1.0f )
	{
		return 0.0f;
    }
	
	return acosf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::ATan( float x )
{
	return atanf(x);
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::ATan( float y, float x )
{
	return atan2f( y, x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Log( float x )
{
	return logf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Exp( float x )
{
	return expf( x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Pow( float x, float y )
{
	return powf( x, y );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Fabs( float x )
{
    *(int *)&x &= 0x7FFFFFFF;
	return x;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Frac( float x )
{
    return ( x - floorf( x ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieMath::IsPowerOfTwo( int i )
{
	return ( i > 0 && !( i & ( i - 1 ) ) );
} 
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::Impulse( float k, float x )
{
    // Great for triggering behaviours or making envelopes for music or animation, and for anything that grows fast and then slowly decays. 
	// Use k to control the streching o the function. Btw, it's maximun, which is 1.0, happens at exactly x = 1/k.
	// Useful for object trajectors such as plane fall.
    const float h = k * x;
    return h * expf( 1.0f-h );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::AlmostIdentity( float x, float m, float n )
{
    // Set m to be your threshold (anything above m stays unchanged), and n the value things will take when your value is zero. Then set
    // p(0) = n
    // p(m) = m
    // p2(0) = 0
    // p2(m) = 1
    if( x > m ) 
	{
	    return x;
	}

    const float a = 2.0f * n - m;
    const float b = 2.0f * m - 3.0f * n;
    const float t = x / m;

    return ( a * t + b ) * t * t + n;
}
// -------------------------------------------------------------------------------------------------------------
// Cheap replacement for a gaussian.
ILINE float ieMath::CubicPulse( float c, float w, float x )
{
    // c - Height of wave
	// w - Width of half of wave width
    x = fabs( x - c );
	
    if( x > w ) 
	{
	    return 0.0f;
	}
	
    x /= w;
	
    return 1.0f - x * x* ( 3.0f - 2.0f * x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE char ieMath::ClampChar( int value)
{
	if( value < -128 )
	{
		return -128;
	}

	if( value > 127 )
	{
		return 127;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE byte ieMath::ClampByte( int value )
{
	if( value < 0 )
	{
		return 0;
	}

	if( value > 255 )
	{
		return 255;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE short ieMath::ClampShort( int value )
{
	if( value < -32768 )
	{
		return -32768;
	}

	if( value > 32767 )
	{
		return 32767;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE word ieMath::ClampWord( int value )
{
	if( value < 0 )
	{
		return 0;
	}

	if( value > 65535 )
	{
		return 65535;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE int ieMath::ClampInt( int value, int min, int max )
{
	if( value < min )
	{
		return min;
	}

	if( value > max )
	{
		return max;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieMath::ClampFloat( float value, float min, float max )
{
	if( value < min )
	{
		return min;
	}

	if( value > max )
	{
		return max;
	}

	return value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE char ieMath::FloatToChar( float f )
{
#if defined SIMD_X86

	__m128 xmm;
	int	   i;

	xmm = _mm_load_ss( &f );
	xmm = _mm_max_ss( xmm, _mm_set_ss( -128.0f ) );
	xmm = _mm_min_ss( xmm, _mm_set_ss( 127.0f ) );
	i = _mm_cvtt_ss2si( xmm );

	return i;
#else
	int	i = 0;

	i = (int)f;

	if( i < -128 )
	{
		return -128;
	}
	
	if( i > 127 )
	{
		return 127;
    }
	
	return i;
#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE byte ieMath::FloatToByte( float f )
{
#if defined SIMD_X86
	__m128 xmm;
	int	   i;

	xmm = _mm_load_ss( &f );
	xmm = _mm_max_ss( xmm, _mm_set_ss( 0.0f ) );
	xmm = _mm_min_ss( xmm, _mm_set_ss( 255.0f ) );
	i = _mm_cvtt_ss2si( xmm );

	return i;
#else
	int	i = 0;

	i = (int)f;

	if( i < 0 )
	{
		return 0;
    }
	
	if( i > 255 )
	{
		return 255;
    }
	
	return i;
#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE short ieMath::FloatToShort( float f )
{
#if defined SIMD_X86
	__m128 xmm;
	int	   i;

	xmm = _mm_load_ss( &f );
	xmm = _mm_max_ss( xmm, _mm_set_ss( -32768.0f ) );
	xmm = _mm_min_ss( xmm, _mm_set_ss( 32767.0f ) );
	i = _mm_cvtt_ss2si( xmm );

	return i;
#else
	int	i = 0;

	i = (int)f;

	if( i < -32768 )
	{
		return -32768;
	}

	if( i > 32767 )
	{
		return 32767;
	}

	return i;
#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE word ieMath::FloatToWord( float f )
{
#if defined SIMD_X86
	__m128 xmm;
	int	   i;

	xmm = _mm_load_ss( &f );
	xmm = _mm_max_ss( xmm, _mm_set_ss( 0.0f ) );
	xmm = _mm_min_ss( xmm, _mm_set_ss( 65535.0f ) );
	i = _mm_cvtt_ss2si( xmm );

	return i;
#else

	int	i = 0;

	i = (int)f;

	if( i < 0 )
	{
		return 0;
    }
	
	if( i > 65535 )
	{
		return 65535;
    }
	
	return i;
#endif
}
// -------------------------------------------------------------------------------------------------------------
ILINE int ieMath::FloatToInt( float f )
{
#if defined SIMD_X86
	__m128 xmm;
	int	   i;

	xmm = _mm_load_ss( &f );
	i = _mm_cvtt_ss2si( xmm );

	return i;
#elif defined _WIN32
    int i;
	__asm fld		f
	__asm fistp		i
	return i;
#else
	return (int) f;
#endif
}
// -------------------------------------------------------------------------------------------------------------

#endif // __IMMENSEENGINE_MATH_H